package com.liuxm.es.test;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.liuxm.es.User;
import org.apache.http.HttpHost;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

/**
 * @author liuxm
 * @version 1.0
 * @date 2021/4/13 19:54
 */
public class ElasticSearchOperate {

    RestHighLevelClient client;

    @Before
    public void before() {
        HttpHost httpHost = new HttpHost("81.68.129.118", 9200, "http");
        RestClientBuilder restClient = RestClient.builder(httpHost);
        client = new RestHighLevelClient(restClient);

    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    @Test
    public void createIndex() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest("user");
        Settings.Builder settingBuilder = Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 1);

        request.settings(settingBuilder);
        request.mapping("{\n" +
                "\t\"properties\": {\n" +
                "\t\t\"id\": {\n" +
                "\t\t\t\"type\": \"long\"\n" +
                "\t\t},\n" +
                "\t\t\"name\": {\n" +
                "\t\t\t\"type\": \"keyword\"\n" +
                "\t\t},\n" +
                "\t\t\"age\": {\n" +
                "\t\t\t\"type\": \"integer\"\n" +
                "\t\t},\n" +
                "\t\t\"gender\": {\n" +
                "\t\t\t\"type\": \"keyword\"\n" +
                "\t\t},\n" +
                "\t\t\"note\": {\n" +
                "\t\t\t\"type\": \"text\",\n" +
                "\t\t\t\"analyzer\": \"ik_max_word\"\n" +
                "\t\t}\n" +
                "\t}\n" +
                "}", XContentType.JSON);

        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        System.out.println("结果：" + response.isAcknowledged());
    }

    @Test
    public void deleteIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest("user");
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        System.out.println("结果：" + delete.isAcknowledged());
    }

    @Test
    public void existsIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest("user");
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println("结果：" + exists);
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Test
    public void createDocument() throws IOException {
        User user = new User(1L, "鲁班", 18, "1", "鲁班在河道唱着歌");
        String jsonStr = JSONUtil.toJsonStr(user);

        IndexRequest request = new IndexRequest("user");
        request.source(jsonStr, XContentType.JSON);
        request.id(user.getId().toString());
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        DocWriteResponse.Result result = response.getResult();
        System.out.println("结果：" + result);
    }

    @Test
    public void modifyDocument() throws IOException {

        GetRequest getRequest = new GetRequest("user", "1");
        GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
        User user = JSONUtil.toBean(getResponse.getSourceAsString(), User.class);


        user.setName("后羿");
        user.setAge(40);
        user.setGender("1");
        user.setNote("射日");


        IndexRequest request = new IndexRequest("user");
        request.id(user.getId().toString());
        request.source(JSONUtil.toJsonStr(user), XContentType.JSON);
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        DocWriteResponse.Result result = response.getResult();
        System.out.println("结果：" + result);
    }

    @Test
    public void removeDocument() throws IOException {
        DeleteRequest request = new DeleteRequest("user", "1");
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        System.out.println("结果:" + response.getResult());
    }

    @Test
    public void searchDocument() throws IOException {
        GetRequest request = new GetRequest("user", "1");
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        System.out.println("结果：" + response.getSourceAsString());
    }

    @Test
    public void createBulkDocument() throws IOException {
        // 需要导入的集合数据
        List<User> userList = Arrays.asList(
                new User(1L, "Rose", 18, "1", "Rose同学在传智播客学表演"),
                new User(2L, "Jack", 38, "1", "Jack同学在黑马学JavaEE"),
                new User(3L, "小红", 23, "0", "小红同学在传智播客学唱歌"),
                new User(4L, "小明", 20, "1", "小明同学在黑马学JavaSE"),
                new User(5L, "达摩", 33, "1", "达摩和尚在达摩院学唱歌"),
                new User(6L, "鲁班", 24, "1", "鲁班同学走在乡间小路上"),
                new User(7L, "孙尚香", 26, "0", "孙尚香同学想带阿斗回东吴"),
                new User(8L, "李白", 27, "1", "李白同学在山顶喝着酒唱着歌"),
                new User(9L, "甄姬", 28, "0", "甄姬同学弹奏一曲东风破"),
                new User(10L, "虞姬", 27, "0", "虞姬同学在和项羽谈情说爱")
        );

        BulkRequest request = new BulkRequest("user");
        userList.forEach(user -> {
            IndexRequest indexRequest = new IndexRequest();
            indexRequest.id(user.getId().toString());
            indexRequest.source(JSONUtil.toJsonStr(user), XContentType.JSON);
            request.add(indexRequest);
        });
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        RestStatus status = response.status();
        System.out.println("结果：" + status);

    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Test
    public void searchWithPageAndSort() throws IOException {
        int page = 1;
        int size = 2;


        SearchRequest request = new SearchRequest("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();

        sourceBuilder.query(matchAllQueryBuilder);
        sourceBuilder.sort("age", SortOrder.ASC);
        sourceBuilder.from((page - 1) * size);
        sourceBuilder.size(size);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits searchHits = response.getHits();
        long total = searchHits.getTotalHits().value;
        for (SearchHit document : searchHits.getHits()) {
            User user = JSONUtil.toBean(document.getSourceAsString(), User.class);
            System.out.println(user);
        }
    }

    @Test
    public void searchQueryMatchAll() throws IOException {
        SearchRequest request = new SearchRequest("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(matchAllQueryBuilder);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        SearchHits searchHits = response.getHits();
        long total = searchHits.getTotalHits().value;
        System.out.println("数据条数：" + total);
        for (SearchHit document : searchHits.getHits()) {

            User user = JSONUtil.toBean(document.getSourceAsString(), User.class);
            System.out.println(user);

        }

    }

    @Test
    public void searchQueryMatch() throws IOException {
        SearchRequest searchRequest = new SearchRequest("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "同学");
        matchQueryBuilder.minimumShouldMatch("80%");
        sourceBuilder.query(matchQueryBuilder);
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits searchHits = searchResponse.getHits();
        long total = searchHits.getTotalHits().value;
        System.out.println("数据条数:" + total);
        for (SearchHit document : searchHits.getHits()) {
            User user = JSONUtil.toBean(document.getSourceAsString(), User.class);
            System.out.println(user);
        }
    }

    @Test
    public void searchQueryTerm() throws IOException {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("note", "达摩和尚在达摩院学唱歌");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(termQueryBuilder);
        printCommon(sourceBuilder);

    }

    @Test
    public void searchQueryRange() throws IOException {
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gte(10).lte(30);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(rangeQueryBuilder);
        printCommon(sourceBuilder);
    }

    @Test
    public void searchQueryFuzzy() throws IOException {
        FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("note", "项羽谈");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(fuzzyQueryBuilder);
        printCommon(sourceBuilder);
    }

    @Test
    public void searchQueryBoolen() throws IOException {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "同学");
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gte(18).lte(28);
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("gender", "1");

        boolQueryBuilder.must(matchQueryBuilder);
        boolQueryBuilder.filter(rangeQueryBuilder);
        boolQueryBuilder.filter(termQueryBuilder);

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);

        printCommon(sourceBuilder);

    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Test
    public void searchQueryHighLight() throws IOException {
        SearchRequest searchRequest = new SearchRequest("user");
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "爱唱歌");

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<p>");
        highlightBuilder.postTags("</p>");
        highlightBuilder.field("note");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(matchQueryBuilder);
        sourceBuilder.highlighter(highlightBuilder);

        searchRequest.source(sourceBuilder);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits searchHits = searchResponse.getHits();

        long total = searchHits.getTotalHits().value;
        System.out.println("数据条数:" + total);
        for (SearchHit document : searchHits.getHits()) {
            User user = JSONUtil.toBean(document.getSourceAsString(), User.class);


            HighlightField highlightField = document.getHighlightFields().get("note");
            if (ObjectUtil.isNotEmpty(highlightField)) {
                Text[] fragments = highlightField.fragments();
                String text = ArrayUtil.join(fragments, StrUtil.EMPTY);
                user.setNote(text);
            }

            System.out.println(user);
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Test
    public void searchQueryAggregation() throws IOException {
        String fieldName = "gender";
        String termsName = "terms_by_" + fieldName;

        SearchRequest searchRequest = new SearchRequest("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms(termsName).field(fieldName);
        aggregationBuilder.subAggregation(AggregationBuilders.avg("avg_by_age").field("age"));
        sourceBuilder.aggregation(aggregationBuilder);
        sourceBuilder.size(0);

        searchRequest.source(sourceBuilder);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        Terms terms = searchResponse.getAggregations().get(termsName);
        for (Terms.Bucket bucket : terms.getBuckets()) {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();

            System.out.println("keyAsString = " + keyAsString);
            Avg avg_by_age = bucket.getAggregations().get("avg_by_age");
            double value = avg_by_age.getValue();

            System.out.println("key:" + keyAsString + "|value:" + value + "|docCount:" + docCount);

        }

    }

    @After
    public void after() throws IOException {
        client.close();
    }


    private void printCommon(SearchSourceBuilder sourceBuilder) throws IOException {
        SearchRequest request = new SearchRequest("user");
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits searchHits = response.getHits();
        long total = searchHits.getTotalHits().value;
        System.out.println("数据条数:" + total);
        for (SearchHit document : searchHits.getHits()) {
            User user = JSONUtil.toBean(document.getSourceAsString(), User.class);
            System.out.println(user);
        }
    }

    private void printHighLight(SearchSourceBuilder sourceBuilder, String... fields) throws IOException, IllegalAccessException {
        SearchRequest request = new SearchRequest("user");
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits searchHits = response.getHits();
        long total = searchHits.getTotalHits().value;
        System.out.println("数据条数：" + total);
        for (SearchHit document : searchHits.getHits()) {
            User user = JSONUtil.toBean(document.getSourceAsString(), User.class);
            for (String field : fields) {
                HighlightField highlightField = document.getHighlightFields().get(field);
                if (ObjectUtil.isNotEmpty(highlightField)) {
                    Text[] fragments = highlightField.getFragments();
                    String highLightText = ArrayUtil.join(fragments, "");
                    // 赋值
                    user = setValue(user, field, highLightText);
                    System.out.println(user);
                }
            }
        }
    }

    private <T> T setValue(T t, String name, Object text) throws IllegalAccessException {
        Class<T> clazz = ClassUtil.getClass(t);
        Field[] fields = ClassUtil.getDeclaredFields(clazz);
        for (Field field : fields) {
            String fieldName = field.getName();
            if (ObjectUtil.equal(fieldName, name)) {
                field.setAccessible(true);
                field.set(t, text);
            }
        }
        return t;
    }
}
